home *** CD-ROM | disk | FTP | other *** search
/ Technotools / Technotools (Chestnut CD-ROM)(1993).ISO / unix / xyzmodem / sz.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-07-16  |  29.0 KB  |  1,428 lines

  1. #define VERSION "sz 1.31 05-29-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*
  5.  *
  6.  * sz.c By Chuck Forsberg
  7.  *
  8.  *    cc -O sz.c -o sz        USG (SYS III/V) Unix
  9.  *    cc -O -DSVR2 sz.c -o sz        Sys V Release 2 with non-blocking input
  10.  *                    Define to allow reverse channel checking
  11.  *    cc -O -DV7  sz.c -o sz        Unix Version 7, 2.8 - 4.3 BSD
  12.  *
  13.  *    cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz    Xenix
  14.  *
  15.  *    ln sz sb            **** All versions ****
  16.  *    ln sz sx            **** All versions ****
  17.  *
  18.  *
  19.  *  ******* Some systems (Venix, Coherent, Regulus) do not *******
  20.  *  ******* support tty raw mode read(2) identically to    *******
  21.  *  ******* Unix. ONEREAD must be defined to force one     *******
  22.  *  ******* character reads for these systems.           *******
  23.  *
  24.  * A program for Unix to send files and commands to computers running
  25.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM.
  26.  *
  27.  *  Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM.
  28.  *
  29.  *  USG UNIX (3.0) ioctl conventions courtesy Jeff Martin
  30. */
  31.  
  32.  
  33. char *substr(), *getenv();
  34.  
  35. #define LOGFILE "/tmp/szlog"
  36. #define zperr vfile
  37.  
  38. #include <stdio.h>
  39. #include <signal.h>
  40. #include <setjmp.h>
  41. #include <ctype.h>
  42.  
  43. #define PATHLEN 256
  44. #define OK 0
  45. #define FALSE 0
  46. #define TRUE 1
  47. #define ERROR (-1)
  48.  
  49. #define HOWMANY 2
  50. int Zmodem=0;        /* ZMODEM protocol requested */
  51. unsigned Baudrate;
  52. unsigned Txwindow;    /* Control the size of the transmitted window */
  53. unsigned Txwspac;    /* Spacing between zcrcq requests */
  54. unsigned Txwcnt;    /* Counter used to space ack requests */
  55. long Lrxpos;        /* Receiver's last reported offset */
  56. int Fromcu = 0;        /* Were called from cu or yam */
  57. #include "rbsb.c"    /* most of the system dependent stuff here */
  58.  
  59. /*
  60.  * Attention string to be executed by receiver to interrupt streaming data
  61.  *  when an error is detected.  A pause (0336) may be needed before the
  62.  *  ^C (03) or after it.
  63.  */
  64. #ifdef READCHECK
  65. char Myattn[] = { 0 };
  66. #else
  67. #ifdef USG
  68. char Myattn[] = { 03, 0336, 0 };
  69. #else
  70. char Myattn[] = { 0 };
  71. #endif
  72. #endif
  73.  
  74. FILE *in;
  75.  
  76. /* Ward Christensen / CP/M parameters - Don't change these! */
  77. #define ENQ 005
  78. #define CAN ('X'&037)
  79. #define XOFF ('s'&037)
  80. #define XON ('q'&037)
  81. #define SOH 1
  82. #define STX 2
  83. #define EOT 4
  84. #define ACK 6
  85. #define NAK 025
  86. #define CPMEOF 032
  87. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  88. #define WANTG 0107    /* Send G not NAK to get nonstop batch xmsn */
  89. #define TIMEOUT (-2)
  90. #define RCDO (-3)
  91. #define RETRYMAX 10
  92. #define SECSIZ 128    /* cp/m's Magic Number record size */
  93. #define KSIZE 1024
  94.  
  95. char Lastrx;
  96. char Crcflg;
  97. int Wcsmask=0377;
  98. int Verbose=0;
  99. int Modem=0;        /* MODEM - don't send pathnames */
  100. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  101. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  102. int Ascii=0;        /* Add CR's for brain damaged programs */
  103. int Fullname=0;        /* transmit full pathname */
  104. int Unlinkafter=0;    /* Unlink file after it is sent */
  105. int Dottoslash=0;    /* Change foo.bar.baz to foo/bar/baz */
  106. int firstsec;
  107. int errcnt=0;        /* number of files unreadable */
  108. int blklen=SECSIZ;        /* length of transmitted records */
  109. int Optiong;        /* Let it rip no wait for sector ACK's */
  110. int Noeofseen;
  111. int Totsecs;        /* total number of sectors this file */
  112. char txbuf[KSIZE];
  113. int Filcnt=0;        /* count of number of files opened */
  114. int Lfseen=0;
  115. unsigned Rxbuflen = 16384;    /* Receiver's max buffer length */
  116. int Tframlen = 0;    /* Override for tx frame length */
  117. int blkopt=0;        /* Override value for zmodem blklen */
  118. int Rxflags = 0;
  119. int Wantfcs32 = TRUE;    /* want to send 32 bit FCS */
  120. char Lzconv;    /* Local ZMODEM file conversion request */
  121. char Lzmanag;    /* Local ZMODEM file management request */
  122. int Lskipnocor;
  123. char Lztrans;
  124. char zconv;        /* ZMODEM file conversion request */
  125. char zmanag;        /* ZMODEM file management request */
  126. char ztrans;        /* ZMODEM file transport request */
  127. int Command;        /* Send a command, then exit. */
  128. char *Cmdstr;        /* Pointer to the command string */
  129. int Cmdtries = 11;
  130. int Cmdack1;        /* Rx ACKs command, then do it */
  131. int Exitcode;
  132. int Testattn;        /* Force receiver to send Attn, etc with qbf. */
  133. char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n";
  134. long Lastread;        /* Beginning offset of last buffer read */
  135. int Lastn;        /* Count of last buffer read or -1 */
  136. int Dontread;        /* Don't read the buffer, it's still there */
  137.  
  138. jmp_buf tohere;        /* For the interrupt on RX timeout */
  139. jmp_buf intrjmp;    /* For the interrupt on RX CAN */
  140.  
  141. /* called by signal interrupt or terminate to clean things up */
  142. bibi(n)
  143. {
  144.     canit(); fflush(stdout); mode(0);
  145.     fprintf(stderr, "sz: caught signal %d; exiting\n", n);
  146.     if (n == SIGQUIT)
  147.         abort();
  148.     exit(128+n);
  149. }
  150. /* Called when Zmodem gets an interrupt (^X) */
  151. onintr()
  152. {
  153.     signal(SIGINT, SIG_IGN);
  154.     longjmp(intrjmp, -1);
  155. }
  156.  
  157.  
  158. #define sendline(c) putchar(c & Wcsmask)
  159.  
  160. #define xsendline(c) putchar(c)
  161.  
  162. flushmo()
  163. {
  164.     fflush(stdout);
  165. }
  166.  
  167. int Zctlesc;    /* Encode control characters */
  168. int Nozmodem = 0;    /* If invoked as "sb" */
  169. char *Progname = "sz";
  170. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  171. #include "zm.c"
  172.  
  173.  
  174. main(argc, argv)
  175. char *argv[];
  176. {
  177.     register char *cp;
  178.     register npats;
  179.     int agcnt; char **agcv;
  180.     char **patts;
  181.     static char xXbuf[BUFSIZ];
  182.  
  183.     if ((cp = getenv("ZNULLS")) && *cp)
  184.         Znulls = atoi(cp);
  185.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  186.         Restricted=TRUE;
  187.     chkinvok(argv[0]);
  188.  
  189.     Rxtimeout = 600;
  190.     npats=0;
  191.     if (argc<2)
  192.         usage();
  193.     setbuf(stdout, xXbuf);        
  194.     while (--argc) {
  195.         cp = *++argv;
  196.         if (*cp++ == '-' && *cp) {
  197.             while ( *cp) {
  198.                 switch(*cp++) {
  199.                 case '+':
  200.                     Lzmanag = ZMAPND; break;
  201.                 case '1':
  202.                     iofd = 1; break;
  203. #ifdef CSTOPB
  204.                 case '2':
  205.                     Twostop = TRUE; break;
  206. #endif
  207.                 case '7':
  208.                     Wcsmask=0177; break;
  209.                 case 'a':
  210.                     Lzconv = ZCNL;
  211.                     Ascii = TRUE; break;
  212.                 case 'b':
  213.                     Lzconv = ZCBIN; break;
  214.                 case 'C':
  215.                     if (--argc < 1) {
  216.                         usage();
  217.                     }
  218.                     Cmdtries = atoi(*++argv);
  219.                     break;
  220.                 case 'i':
  221.                     Cmdack1 = ZCACK1;
  222.                     /* **** FALL THROUGH TO **** */
  223.                 case 'c':
  224.                     if (--argc != 1) {
  225.                         usage();
  226.                     }
  227.                     Command = TRUE;
  228.                     Cmdstr = *++argv;
  229.                     break;
  230.                 case 'd':
  231.                     ++Dottoslash;
  232.                     /* **** FALL THROUGH TO **** */
  233.                 case 'f':
  234.                     Fullname=TRUE; break;
  235.                 case 'e':
  236.                     Zctlesc = 1; break;
  237.                 case 'k':
  238.                     blklen=KSIZE; break;
  239.                 case 'L':
  240.                     if (--argc < 1) {
  241.                         usage();
  242.                     }
  243.                     blkopt = atoi(*++argv);
  244.                     if (blkopt<24 || blkopt>1024)
  245.                         usage();
  246.                     break;
  247.                 case 'l':
  248.                     if (--argc < 1) {
  249.                         usage();
  250.                     }
  251.                     Tframlen = atoi(*++argv);
  252.                     if (Tframlen<32 || Tframlen>1024)
  253.                         usage();
  254.                     break;
  255.                 case 'N':
  256.                     Lzmanag = ZMNEWL;  break;
  257.                 case 'n':
  258.                     Lzmanag = ZMNEW;  break;
  259.                 case 'o':
  260.                     Wantfcs32 = FALSE; break;
  261.                 case 'p':
  262.                     Lzmanag = ZMPROT;  break;
  263.                 case 'r':
  264.                     Lzconv = ZCRESUM;
  265.                 case 'q':
  266.                     Quiet=TRUE; Verbose=0; break;
  267.                 case 't':
  268.                     if (--argc < 1) {
  269.                         usage();
  270.                     }
  271.                     Rxtimeout = atoi(*++argv);
  272.                     if (Rxtimeout<10 || Rxtimeout>1000)
  273.                         usage();
  274.                     break;
  275.                 case 'T':
  276.                     Testattn = TRUE; break;
  277.                 case 'u':
  278.                     ++Unlinkafter; break;
  279.                 case 'v':
  280.                     ++Verbose; break;
  281.                 case 'w':
  282.                     if (--argc < 1) {
  283.                         usage();
  284.                     }
  285.                     Txwindow = atoi(*++argv);
  286.                     if (Txwindow < 256)
  287.                         Txwindow = 256;
  288.                     Txwindow = (Txwindow/64) * 64;
  289.                     Txwspac = Txwindow/4;
  290.                     if (blkopt > Txwspac
  291.                      || (!blkopt && Txwspac < 1024))
  292.                         blkopt = Txwspac;
  293.                     break;
  294.                 case 'X':
  295.                     ++Modem; break;
  296.                 case 'Y':
  297.                     Lskipnocor = TRUE;
  298.                     /* **** FALLL THROUGH TO **** */
  299.                 case 'y':
  300.                     Lzmanag = ZMCLOB; break;
  301.                 default:
  302.                     usage();
  303.                 }
  304.             }
  305.         }
  306.         else if ( !npats && argc>0) {
  307.             if (argv[0][0]) {
  308.                 npats=argc;
  309.                 patts=argv;
  310.                 if ( !strcmp(*patts, "-"))
  311.                     iofd = 1;
  312.             }
  313.         }
  314.     }
  315.     if (npats < 1 && !Command) 
  316.         usage();
  317.     if (Verbose) {
  318.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  319.             printf("Can't open log file %s\n",LOGFILE);
  320.             exit(0200);
  321.         }
  322.         setbuf(stderr, NULL);
  323.     }
  324.     if ((Fromcu=from_cu()) && !Quiet) {
  325.         if (Verbose == 0)
  326.             Verbose = 2;
  327.     }
  328.  
  329.     mode(1);
  330.  
  331.     if (signal(SIGINT, bibi) == SIG_IGN) {
  332.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  333.     } else {
  334.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  335.     }
  336.     if ( !Fromcu)
  337.         signal(SIGQUIT, SIG_IGN);
  338.     signal(SIGTERM, bibi);
  339.  
  340.     if ( !Modem) {
  341.         if (!Nozmodem) {
  342.             printf("rz\r");  fflush(stdout);
  343.         }
  344.         if (!Command && !Quiet && Verbose != 1) {
  345.             fprintf(stderr, "%s: %d file%s requested:\r\n",
  346.              Progname, npats, npats>1?"s":"");
  347.             for ( agcnt=npats, agcv=patts; --agcnt>=0; ) {
  348.                 fprintf(stderr, "%s ", *agcv++);
  349.             }
  350.             fprintf(stderr, "\r\n");
  351.             printf("\r\n\bSending in Batch Mode\r\n");
  352.         }
  353.         if (!Nozmodem) {
  354.             stohdr(0L);
  355.             if (Command)
  356.                 Txhdr[ZF0] = ZCOMMAND;
  357.             zshhdr(ZRQINIT, Txhdr);
  358.         }
  359.     }
  360.     fflush(stdout);
  361.  
  362.     if (Command) {
  363.         if (getzrxinit()) {
  364.             Exitcode=0200; canit();
  365.         }
  366.         else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  367.             Exitcode=0200; canit();
  368.         }
  369.     } else if (wcsend(npats, patts)==ERROR) {
  370.         Exitcode=0200;
  371.         canit();
  372.     }
  373.     fflush(stdout);
  374.     mode(0);
  375.     exit((errcnt != 0) | Exitcode);
  376.     /*NOTREACHED*/
  377. }
  378.  
  379. wcsend(argc, argp)
  380. char *argp[];
  381. {
  382.     register n;
  383.  
  384.     Crcflg=FALSE;
  385.     firstsec=TRUE;
  386.     for (n=0; n<argc; ++n) {
  387.         Totsecs = 0;
  388.         if (wcs(argp[n])==ERROR)
  389.             return ERROR;
  390.     }
  391.     Totsecs = 0;
  392.     if (Filcnt==0) {    /* bitch if we couldn't open ANY files */
  393.         if (1) {
  394.             Command = TRUE;
  395.             Cmdstr = "echo \"sz: Can't open any requested files\"";
  396.             if (getnak()) {
  397.                 Exitcode=0200; canit();
  398.             }
  399.             if (!Zmodem)
  400.                 canit();
  401.             else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) {
  402.                 Exitcode=0200; canit();
  403.             }
  404.             Exitcode = 1; return OK;
  405.         }
  406. /*        canit();
  407.         fprintf(stderr,"\r\nCan't open any requested files.\r\n");
  408.         return ERROR;
  409. */    }
  410.     if (Zmodem)
  411.         saybibi();
  412.     else
  413.         wctxpn("");
  414.     return OK;
  415. }
  416.  
  417. wcs(oname)
  418. char *oname;
  419. {
  420.     register c;
  421.     register char *p;
  422.     struct stat f;
  423.     char name[PATHLEN];
  424.  
  425.     strcpy(name, oname);
  426.  
  427.     if (Restricted) {
  428.         /* restrict pathnames to current tree or uucppublic */
  429.         if ( substr(name, "../")
  430.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  431.             canit();
  432.             fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n");
  433.             return ERROR;
  434.         }
  435.     }
  436.  
  437.     if ( !strcmp(oname, "-")) {
  438.         if ((p = getenv("ONAME")) && *p)
  439.             strcpy(name, p);
  440.         else
  441.             sprintf(name, "s%d.sz", getpid());
  442.         in = stdin;
  443.     }
  444.     else if ((in=fopen(oname, "r"))==NULL) {
  445.         ++errcnt;
  446.         return OK;    /* pass over it, there may be others */
  447.     }
  448.     ++Noeofseen;  Lastread = 0;  Lastn = -1; Dontread = FALSE;
  449.     /* Check for directory or block special files */
  450.     fstat(fileno(in), &f);
  451.     c = f.st_mode & S_IFMT;
  452.     if (c == S_IFDIR || c == S_IFBLK) {
  453.         fclose(in);
  454.         return OK;
  455.     }
  456.  
  457.     ++Filcnt;
  458.     switch (wctxpn(name)) {
  459.     case ERROR:
  460.         return ERROR;
  461.     case ZSKIP:
  462.         return OK;
  463.     }
  464.     if (!Zmodem && wctx()==ERROR)
  465.         return ERROR;
  466.     if (Unlinkafter)
  467.         unlink(oname);
  468.     return 0;
  469. }
  470.  
  471. /*
  472.  * generate and transmit pathname block consisting of
  473.  *  pathname (null terminated),
  474.  *  file length, mode time and file mode in octal
  475.  *  as provided by the Unix fstat call.
  476.  *  N.B.: modifies the passed name, may extend it!
  477.  */
  478. wctxpn(name)
  479. char *name;
  480. {
  481.     register char *p, *q;
  482.     char name2[PATHLEN];
  483.     struct stat f;
  484.  
  485.     if (Modem) {
  486.         if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) {
  487.             fprintf(stderr, "Sending %s, %ld blocks: ",
  488.               name, f.st_size>>7);
  489.         }
  490.         fprintf(stderr, "Give your local XMODEM receive command now.\r\n");
  491.         return OK;
  492.     }
  493.     logent("\r\nAwaiting pathname nak for %s\r\n", *name?name:"<END>");
  494.     if ( !Zmodem)
  495.         if (getnak())
  496.             return ERROR;
  497.  
  498.     q = (char *) 0;
  499.     if (Dottoslash) {        /* change . to . */
  500.         for (p=name; *p; ++p) {
  501.             if (*p == '/')
  502.                 q = p;
  503.             else if (*p == '.')
  504.                 *(q=p) = '/';
  505.         }
  506.         if (q && strlen(++q) > 8) {    /* If name>8 chars */
  507.             q += 8;            /*   make it .ext */
  508.             strcpy(name2, q);    /* save excess of name */
  509.             *q = '.';
  510.             strcpy(++q, name2);    /* add it back */
  511.         }
  512.     }
  513.  
  514.     for (p=name, q=txbuf ; *p; )
  515.         if ((*q++ = *p++) == '/' && !Fullname)
  516.             q = txbuf;
  517.     *q++ = 0;
  518.     p=q;
  519.     while (q < (txbuf + KSIZE))
  520.         *q++ = 0;
  521.     if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1)
  522.         sprintf(p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
  523.     /* force 1k blocks if name won't fit in 128 byte block */
  524.     if (txbuf[125])
  525.         blklen=KSIZE;
  526.     else {        /* A little goodie for IMP/KMD */
  527.         if (Zmodem)
  528.             blklen = SECSIZ;
  529.         txbuf[127] = (f.st_size + 127) >>7;
  530.         txbuf[126] = (f.st_size + 127) >>15;
  531.     }
  532.     if (Zmodem)
  533.         return zsendfile(txbuf, 1+strlen(p)+(p-txbuf));
  534.     if (wcputsec(txbuf, 0, SECSIZ)==ERROR)
  535.         return ERROR;
  536.     return OK;
  537. }
  538.  
  539. getnak()
  540. {
  541.     register firstch;
  542.  
  543.     Lastrx = 0;
  544.     for (;;) {
  545.         switch (firstch = readock(800,1)) {
  546.         case ZPAD:
  547.             if (getzrxinit())
  548.                 return ERROR;
  549.             Ascii = 0;
  550.             return FALSE;
  551.         case TIMEOUT:
  552.             logent("Timeout on pathname\n");
  553.             return TRUE;
  554.         case WANTG:
  555. #ifdef USG
  556.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  557. #endif
  558.             Optiong = TRUE;
  559.             blklen=KSIZE;
  560.         case WANTCRC:
  561.             Crcflg = TRUE;
  562.         case NAK:
  563.             return FALSE;
  564.         case CAN:
  565.             if ((firstch = readock(20,1)) == CAN && Lastrx == CAN)
  566.                 return TRUE;
  567.         default:
  568.             break;
  569.         }
  570.         Lastrx = firstch;
  571.     }
  572. }
  573.  
  574.  
  575. wctx()
  576. {
  577.     register int sectnum, attempts, firstch;
  578.  
  579.     firstsec=TRUE;
  580.  
  581.     while ((firstch=readock(Rxtimeout, 2))!=NAK && firstch != WANTCRC
  582.       && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN)
  583.         ;
  584.     if (firstch==CAN) {
  585.         logent("Receiver CANcelled\n");
  586.         return ERROR;
  587.     }
  588.     if (firstch==WANTCRC)
  589.         Crcflg=TRUE;
  590.     if (firstch==WANTG)
  591.         Crcflg=TRUE;
  592.     sectnum=1;
  593.     while (filbuf(txbuf, blklen)) {
  594.         if (wcputsec(txbuf, sectnum, blklen)==ERROR) {
  595.             return ERROR;
  596.         } else
  597.             sectnum++;
  598.     }
  599.     if (Verbose>1)
  600.         fprintf(stderr, " Closing ");
  601.     fclose(in);
  602.     attempts=0;
  603.     do {
  604.         logent(" EOT ");
  605.         purgeline();
  606.         sendline(EOT);
  607.         fflush(stdout);
  608.         ++attempts;
  609.     }
  610.         while ((firstch=(readock(Rxtimeout, 1)) != ACK) && attempts < RETRYMAX);
  611.     if (attempts == RETRYMAX) {
  612.         logent("No ACK on EOT\n");
  613.         return ERROR;
  614.     }
  615.     else
  616.         return OK;
  617. }
  618.  
  619. wcputsec(buf, sectnum, cseclen)
  620. char *buf;
  621. int sectnum;
  622. int cseclen;    /* data length of this sector to send */
  623. {
  624.     register checksum, wcj;
  625.     register char *cp;
  626.     unsigned oldcrc;
  627.     int firstch;
  628.     int attempts;
  629.  
  630.     firstch=0;    /* part of logic to detect CAN CAN */
  631.  
  632.     if (Verbose>1)
  633.         fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 );
  634.     for (attempts=0; attempts <= RETRYMAX; attempts++) {
  635.         Lastrx= firstch;
  636.         sendline(cseclen==KSIZE?STX:SOH);
  637.         sendline(sectnum);
  638.         sendline(-sectnum -1);
  639.         oldcrc=checksum=0;
  640.         for (wcj=cseclen,cp=buf; --wcj>=0; ) {
  641.             sendline(*cp);
  642.             oldcrc=updcrc((0377& *cp), oldcrc);
  643.             checksum += *cp++;
  644.         }
  645.         if (Crcflg) {
  646.             oldcrc=updcrc(0,updcrc(0,oldcrc));
  647.             sendline((int)oldcrc>>8);
  648.             sendline((int)oldcrc);
  649.         }
  650.         else
  651.             sendline(checksum);
  652.  
  653.         if (Optiong) {
  654.             firstsec = FALSE; return OK;
  655.         }
  656.         firstch = readock(Rxtimeout, (Noeofseen&§num) ? 2:1);
  657. gotnak:
  658.         switch (firstch) {
  659.         case CAN:
  660.             if(Lastrx == CAN) {
  661. cancan:
  662.                 logent("Cancelled\n");  return ERROR;
  663.             }
  664.             break;
  665.         case TIMEOUT:
  666.             logent("Timeout on sector ACK\n"); continue;
  667.         case WANTCRC:
  668.             if (firstsec)
  669.                 Crcflg = TRUE;
  670.         case NAK:
  671.             logent("NAK on sector\n"); continue;
  672.         case ACK: 
  673.             firstsec=FALSE;
  674.             Totsecs += (cseclen>>7);
  675.             return OK;
  676.         case ERROR:
  677.             logent("Got burst for sector ACK\n"); break;
  678.         default:
  679.             logent("Got %02x for sector ACK\n", firstch); break;
  680.         }
  681.         for (;;) {
  682.             Lastrx = firstch;
  683.             if ((firstch = readock(Rxtimeout, 2)) == TIMEOUT)
  684.                 break;
  685.             if (firstch == NAK || firstch == WANTCRC)
  686.                 goto gotnak;
  687.             if (firstch == CAN && Lastrx == CAN)
  688.                 goto cancan;
  689.         }
  690.     }
  691.     logent("Retry Count Exceeded\n");
  692.     return ERROR;
  693. }
  694.  
  695. /* fill buf with count chars padding with ^Z for CPM */
  696. filbuf(buf, count)
  697. register char *buf;
  698. {
  699.     register c, m;
  700.  
  701.     if ( !Ascii) {
  702.         m = read(fileno(in), buf, count);
  703.         if (m <= 0)
  704.             return 0;
  705.         while (m < count)
  706.             buf[m++] = 032;
  707.         return count;
  708.     }
  709.     m=count;
  710.     if (Lfseen) {
  711.         *buf++ = 012; --m; Lfseen = 0;
  712.     }
  713.     while ((c=getc(in))!=EOF) {
  714.         if (c == 012) {
  715.             *buf++ = 015;
  716.             if (--m == 0) {
  717.                 Lfseen = TRUE; break;
  718.             }
  719.         }
  720.         *buf++ =c;
  721.         if (--m == 0)
  722.             break;
  723.     }
  724.     if (m==count)
  725.         return 0;
  726.     else
  727.         while (--m>=0)
  728.             *buf++ = CPMEOF;
  729.     return count;
  730. }
  731. /* fill buf with count chars */
  732. zfilbuf(buf, count)
  733. register char *buf;
  734. {
  735.     register c, m;
  736.  
  737.     m=count;
  738.     while ((c=getc(in))!=EOF) {
  739.         *buf++ =c;
  740.         if (--m == 0)
  741.             break;
  742.     }
  743.     return (count - m);
  744. }
  745.  
  746. /* VARARGS1 */
  747. vfile(f, a, b, c)
  748. register char *f;
  749. {
  750.     if (Verbose > 1) {
  751.         fprintf(stderr, f, a, b, c);
  752.         fprintf(stderr, "\n");
  753.     }
  754. }
  755.  
  756.  
  757. alrm()
  758. {
  759.     longjmp(tohere, -1);
  760. }
  761.  
  762.  
  763. /*
  764.  * readock(timeout, count) reads character(s) from file descriptor 0
  765.  *  (1 <= count <= 3)
  766.  * it attempts to read count characters. If it gets more than one,
  767.  * it is an error unless all are CAN
  768.  * (otherwise, only normal response is ACK, CAN, or C)
  769.  *  Only looks for one if Optiong, which signifies cbreak, not raw input
  770.  *
  771.  * timeout is in tenths of seconds
  772.  */
  773. readock(timeout, count)
  774. {
  775.     register int c;
  776.     static char byt[5];
  777.  
  778.     if (Optiong)
  779.         count = 1;    /* Special hack for cbreak */
  780.  
  781.     fflush(stdout);
  782.     if (setjmp(tohere)) {
  783.         logent("TIMEOUT\n");
  784.         return TIMEOUT;
  785.     }
  786.     c = timeout/10;
  787.     if (c<2)
  788.         c=2;
  789.     if (Verbose>3) {
  790.         fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c);
  791.         byt[1] = 0;
  792.     }
  793.     signal(SIGALRM, alrm); alarm(c);
  794. #ifdef ONEREAD
  795.     c=read(iofd, byt, 1);        /* regulus raw read is unique */
  796. #else
  797.     c=read(iofd, byt, count);
  798. #endif
  799.     alarm(0);
  800.     if (Verbose>5)
  801.         fprintf(stderr, "ret cnt=%d %x %x\n", c, byt[0], byt[1]);
  802.     if (c<1)
  803.         return TIMEOUT;
  804.     if (c==1)
  805.         return (byt[0]&0377);
  806.     else
  807.         while (c)
  808.             if (byt[--c] != CAN)
  809.                 return ERROR;
  810.     return CAN;
  811. }
  812. readline(n)
  813. {
  814.     return (readock(n, 1));
  815. }
  816.  
  817.  
  818. purgeline()
  819. {
  820. #ifdef USG
  821.     ioctl(iofd, TCFLSH, 0);
  822. #else
  823.     lseek(iofd, 0L, 2);
  824. #endif
  825. }
  826.  
  827.  
  828. /* send cancel string to get the other end to shut up */
  829. canit()
  830. {
  831.     static char canistr[] = {
  832.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  833.     };
  834.  
  835.     printf(canistr);
  836.     fflush(stdout);
  837. }
  838.  
  839. /*VARARGS1*/
  840. logent(a, b, c)
  841. char *a, *b, *c;
  842. {
  843.     if(Verbose)
  844.         fprintf(stderr, a, b, c);
  845. }
  846.  
  847. /*
  848.  * return 1 iff stdout and stderr are different devices
  849.  *  indicating this program operating with a modem on a
  850.  *  different line
  851.  */
  852. from_cu()
  853. {
  854.     struct stat a, b;
  855.     fstat(1, &a); fstat(2, &b);
  856.     return (a.st_rdev != b.st_rdev);
  857. }
  858.  
  859. /*
  860.  * substr(string, token) searches for token in string s
  861.  * returns pointer to token within string if found, NULL otherwise
  862.  */
  863. char *
  864. substr(s, t)
  865. register char *s,*t;
  866. {
  867.     register char *ss,*tt;
  868.     /* search for first char of token */
  869.     for (ss=s; *s; s++)
  870.         if (*s == *t)
  871.             /* compare token with substring */
  872.             for (ss=s,tt=t; ;) {
  873.                 if (*tt == 0)
  874.                     return s;
  875.                 if (*ss++ != *tt++)
  876.                     break;
  877.             }
  878.     return NULL;
  879. }
  880.  
  881. char *babble[] = {
  882.     "Send file(s) with ZMODEM/YMODEM/XMODEM Protocol",
  883.     "    (Y) = Option applies to YMODEM only",
  884.     "    (Z) = Option applies to ZMODEM only",
  885.     "Usage:    sz [-12+abdefkLlNnquvwYy] [-] file ...",
  886.     "    sz [-12Ceqv] -c COMMAND",
  887.     "    sb [-12adfkquv] [-] file ...",
  888.     "    sx [-12akquv] [-] file",
  889.     "    1 Use stdout for modem input",
  890. #ifdef CSTOPB
  891.     "    2 Use 2 stop bits",
  892. #endif
  893.     "    + Append to existing destination file (Z)",
  894.     "    a (ASCII) change NL to CR/LF",
  895.     "    b Binary file transfer override",
  896.     "    c send COMMAND (Z)",
  897.     "    d Change '.' to '/' in pathnames (Y/Z)",
  898.     "    e Escape all control characters (Z)",
  899.     "    f send Full pathname (Y/Z)",
  900.     "    i send COMMAND, ack Immediately (Z)",
  901.     "    k Send 1024 byte packets (Y)",
  902.     "    L N Limit subpacket length to N bytes (Z)",
  903.     "    l N Limit frame length to N bytes (l>=L) (Z)",
  904.     "    n send file if source newer (Z)",
  905.     "    N send file if source newer or longer (Z)",
  906.     "    o Use 16 bit CRC instead of 32 bit CRC (Z)",
  907.     "    p Protect existing destination file (Z)",
  908.     "    r Resume/Recover interrupted file transfer (Z)",
  909.     "    q Quiet (no progress reports)",
  910.     "    u Unlink file after transmission",
  911.     "    v Verbose - provide debugging information",
  912.     "    w N Window is N bytes (Z)",
  913.     "    Y Yes, overwrite existing file, skip if not present at rx (Z)",
  914.     "    y Yes, overwrite existing file (Z)",
  915.     "- as pathname sends standard input as sPID.sz or environment ONAME",
  916.     ""
  917. };
  918.  
  919. usage()
  920. {
  921.     char **pp;
  922.  
  923.     for (pp=babble; **pp; ++pp)
  924.         fprintf(stderr, "%s\n", *pp);
  925.     fprintf(stderr, "%s for %s by Chuck Forsberg  ", VERSION, OS);
  926.     exit(1);
  927. }
  928.  
  929. /*
  930.  * Get the receiver's init parameters
  931.  */
  932. getzrxinit()
  933. {
  934.     register n;
  935.     struct stat f;
  936.  
  937.     for (n=10; --n>=0; ) {
  938.         
  939.         switch (zgethdr(Rxhdr, 1)) {
  940.         case ZCHALLENGE:    /* Echo receiver's challenge numbr */
  941.             stohdr(Rxpos);
  942.             zshhdr(ZACK, Txhdr);
  943.             continue;
  944.         case ZCOMMAND:        /* They didn't see out ZRQINIT */
  945.             stohdr(0L);
  946.             zshhdr(ZRQINIT, Txhdr);
  947.             continue;
  948.         case ZRINIT:
  949.             Rxflags = 0377 & Rxhdr[ZF0];
  950.             Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
  951.             Zctlesc |= Rxflags & TESCCTL;
  952.             Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8);
  953.             vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
  954.             if ( !Fromcu)
  955.                 signal(SIGINT, SIG_IGN);
  956. #ifdef USG
  957.             mode(2);    /* Set cbreak, XON/XOFF, etc. */
  958. #endif
  959. #ifndef READCHECK
  960. #ifndef USG
  961.             /* Use 1024 byte frames if no sample/interrupt */
  962.             if (Rxbuflen < 32 || Rxbuflen > 1024) {
  963.                 Rxbuflen = 1024;
  964.                 vfile("Rxbuflen=%d", Rxbuflen);
  965.             }
  966. #endif
  967. #endif
  968.             /* Override to force shorter frame length */
  969.             if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32))
  970.                 Rxbuflen = Tframlen;
  971.             if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024))
  972.                 Rxbuflen = Tframlen;
  973.             vfile("Rxbuflen=%d", Rxbuflen);
  974.  
  975.             /* If using a pipe for testing set lower buf len */
  976.             fstat(iofd, &f);
  977.             if ((f.st_mode & S_IFMT) != S_IFCHR
  978.               && (Rxbuflen == 0 || Rxbuflen > 4096))
  979.                 Rxbuflen = 4096;
  980.             /*
  981.              * If input is not a regular file, force ACK's each 1024
  982.              *  (A smarter strategey could be used here ...)
  983.              */
  984.             fstat(fileno(in), &f);
  985.             if (((f.st_mode & S_IFMT) != S_IFREG)
  986.               && (Rxbuflen == 0 || Rxbuflen > 1024))
  987.                 Rxbuflen = 1024;
  988.             vfile("Rxbuflen=%d", Rxbuflen);
  989.  
  990.             return (sendzsinit());
  991.         case ZCAN:
  992.         case TIMEOUT:
  993.             return ERROR;
  994.         case ZRQINIT:
  995.             if (Rxhdr[ZF0] == ZCOMMAND)
  996.                 continue;
  997.         default:
  998.             zshhdr(ZNAK, Txhdr);
  999.             continue;
  1000.         }
  1001.     }
  1002.     return ERROR;
  1003. }
  1004.  
  1005. /* Send send-init information */
  1006. sendzsinit()
  1007. {
  1008.     register c;
  1009.     register errors;
  1010.  
  1011.     if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL)))
  1012.         return OK;
  1013.     errors = 0;
  1014.     for (;;) {
  1015.         stohdr(0L);
  1016.         if (Zctlesc) {
  1017.             Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr);
  1018.         }
  1019.         else
  1020.             zsbhdr(ZSINIT, Txhdr);
  1021.         zsdata(Myattn, 1+strlen(Myattn), ZCRCW);
  1022.         c = zgethdr(Rxhdr, 1);
  1023.         switch (c) {
  1024.         case ZCAN:
  1025.             return ERROR;
  1026.         case ZACK:
  1027.             return OK;
  1028.         default:
  1029.             if (++errors > 9)
  1030.                 return ERROR;
  1031.             continue;
  1032.         }
  1033.     }
  1034. }
  1035.  
  1036. /* Send file name and related info */
  1037. zsendfile(buf, blen)
  1038. char *buf;
  1039. {
  1040.     register c;
  1041.  
  1042.     for (;;) {
  1043.         Txhdr[ZF0] = Lzconv;    /* file conversion request */
  1044.         Txhdr[ZF1] = Lzmanag;    /* file management request */
  1045.         if (Lskipnocor)
  1046.             Txhdr[ZF1] |= ZMSKNOLOC;
  1047.         Txhdr[ZF2] = Lztrans;    /* file transport request */
  1048.         Txhdr[ZF3] = 0;
  1049.         zsbhdr(ZFILE, Txhdr);
  1050.         zsdata(buf, blen, ZCRCW);
  1051. again:
  1052.         c = zgethdr(Rxhdr, 1);
  1053.         switch (c) {
  1054.         case ZRINIT:
  1055.             while ((c = readline(50)) > 0)
  1056.                 if (c == ZPAD) {
  1057.                     goto again;
  1058.                 }
  1059.             /* **** FALL THRU TO **** */
  1060.         default:
  1061.             continue;
  1062.         case ZCAN:
  1063.         case TIMEOUT:
  1064.         case ZABORT:
  1065.         case ZFIN:
  1066.             return ERROR;
  1067.         case ZSKIP:
  1068.             fclose(in); return c;
  1069.         case ZRPOS:
  1070.             fseek(in, Rxpos, 0);
  1071.             Txpos = Rxpos; Lastn = -1; Dontread = FALSE;
  1072.             return zsendfdata();
  1073.         }
  1074.     }
  1075. }
  1076.  
  1077. /* Send the data in the file */
  1078. zsendfdata()
  1079. {
  1080.     register c, e, n;
  1081.     register newcnt;
  1082.     register long tcount = 0;
  1083.     static int tleft = 6;    /* Counter for test mode */
  1084.  
  1085.     if (Baudrate > 300)
  1086.         blklen = 256;
  1087.     if (Baudrate > 2400)
  1088.         blklen = KSIZE;
  1089.     if (Rxbuflen && blklen>Rxbuflen)
  1090.         blklen = Rxbuflen;
  1091.     if (blkopt && blklen > blkopt)
  1092.         blklen = blkopt;
  1093.     vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
  1094.     vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);
  1095.     Lrxpos = 0;
  1096. somemore:
  1097.     if (setjmp(intrjmp)) {
  1098. waitack:
  1099.         c = getinsync(0);
  1100. gotack:
  1101.         switch (c) {
  1102.         default:
  1103.         case ZCAN:
  1104.             fclose(in);
  1105.             return ERROR;
  1106.         case ZSKIP:
  1107.             fclose(in);
  1108.             return c;
  1109.         case ZACK:
  1110.         case ZRPOS:
  1111.             break;
  1112.         case ZRINIT:
  1113.             return OK;
  1114.         }
  1115. #ifdef READCHECK
  1116.         /*
  1117.          * If the reverse channel can be tested for data,
  1118.          *  this logic may be used to detect error packets
  1119.          *  sent by the receiver, in place of setjmp/longjmp
  1120.          *  rdchk(fdes) returns non 0 if a character is available
  1121.          */
  1122.         while (rdchk(iofd)) {
  1123. #ifdef SVR2
  1124.             switch (checked)
  1125. #else
  1126.             switch (readline(1))
  1127. #endif
  1128.             {
  1129.             case CAN:
  1130.             case ZPAD:
  1131.                 c = getinsync(1);
  1132.                 goto gotack;
  1133.             case XOFF:        /* Wait a while for an XON */
  1134.             case XOFF|0200:
  1135.                 readline(100);
  1136.             }
  1137.         }
  1138. #endif
  1139.     }
  1140.  
  1141.     if ( !Fromcu)
  1142.         signal(SIGINT, onintr);
  1143.     newcnt = Rxbuflen;
  1144.     Txwcnt = 0;
  1145.     stohdr(Txpos);
  1146.     zsbhdr(ZDATA, Txhdr);
  1147.  
  1148.     /*
  1149.      * Special testing mode.  This should force receiver to Attn,ZRPOS
  1150.      *  many times.  Each time the signal should be caught, causing the
  1151.      *  file to be started over from the beginning.
  1152.      */
  1153.     if (Testattn) {
  1154.         if ( --tleft)
  1155.             while (tcount < 20000) {
  1156.                 printf(qbf); fflush(stdout);
  1157.                 tcount += strlen(qbf);
  1158. #ifdef READCHECK
  1159.                 while (rdchk(iofd)) {
  1160. #ifdef SVR2
  1161.                     switch (checked)
  1162. #else
  1163.                     switch (readline(1))
  1164. #endif
  1165.                     {
  1166.                     case CAN:
  1167.                     case ZPAD:
  1168. #ifdef TCFLSH
  1169.                         ioctl(iofd, TCFLSH, 1);
  1170. #endif
  1171.                         goto waitack;
  1172.                     case XOFF:    /* Wait for XON */
  1173.                     case XOFF|0200:
  1174.                         readline(100);
  1175.                     }
  1176.                 }
  1177. #endif
  1178.             }
  1179.         signal(SIGINT, SIG_IGN); canit();
  1180.         sleep(3); purgeline(); mode(0);
  1181.         printf("\nsz: Tcount = %ld\n", tcount);
  1182.         if (tleft) {
  1183.             printf("ERROR: Interrupts Not Caught\n");
  1184.             exit(1);
  1185.         }
  1186.         exit(0);
  1187.     }
  1188.  
  1189.     do {
  1190.         if (Dontread) {
  1191.             n = Lastn;
  1192.         } else {
  1193.             n = zfilbuf(txbuf, blklen);
  1194.             Lastread = Txpos;  Lastn = n;
  1195.         }
  1196.         if (Verbose > 10)
  1197.             vfile("Dontread=%d c=%d", Dontread, n);
  1198.         Dontread = FALSE;
  1199.         if (n < blklen)
  1200.             e = ZCRCE;
  1201.         else if (Rxbuflen && (newcnt -= n) <= 0)
  1202.             e = ZCRCW;
  1203.         else if (Txwindow && (Txwcnt += n) >= Txwspac) {
  1204.             Txwcnt = 0;  e = ZCRCQ;
  1205.         }
  1206.         else
  1207.             e = ZCRCG;
  1208.         zsdata(txbuf, n, e);
  1209.         Txpos += n;
  1210.         if (e == ZCRCW)
  1211.             goto waitack;
  1212. #ifdef READCHECK
  1213.         /*
  1214.          * If the reverse channel can be tested for data,
  1215.          *  this logic may be used to detect error packets
  1216.          *  sent by the receiver, in place of setjmp/longjmp
  1217.          *  rdchk(fdes) returns non 0 if a character is available
  1218.          */
  1219.         fflush(stdout);
  1220.         while (rdchk(iofd)) {
  1221. #ifdef SVR2
  1222.             switch (checked)
  1223. #else
  1224.             switch (readline(1))
  1225. #endif
  1226.             {
  1227.             case CAN:
  1228.             case ZPAD:
  1229.                 c = getinsync(1);
  1230.                 if (c == ZACK)
  1231.                     break;
  1232. #ifdef TCFLSH
  1233.                 ioctl(iofd, TCFLSH, 1);
  1234. #endif
  1235.                 /* zcrce - dinna wanna starta ping-pong game */
  1236.                 zsdata(txbuf, 0, ZCRCE);
  1237.                 goto gotack;
  1238.             case XOFF:        /* Wait a while for an XON */
  1239.             case XOFF|0200:
  1240.                 readline(100);
  1241.             }
  1242.         }
  1243. #endif    /* READCHECK */
  1244.         if (Txwindow) {
  1245.             while ((tcount = Txpos - Lrxpos) >= Txwindow) {
  1246.                 vfile("%ld window >= %u", tcount, Txwindow);
  1247.                 if (e != ZCRCQ)
  1248.                     zsdata(txbuf, 0, e = ZCRCQ);
  1249.                 c = getinsync(1);
  1250.                 if (c != ZACK) {
  1251. #ifdef TCFLSH
  1252.                     ioctl(iofd, TCFLSH, 1);
  1253. #endif
  1254.                     zsdata(txbuf, 0, ZCRCE);
  1255.                     goto gotack;
  1256.                 }
  1257.             }
  1258.             vfile("window = %ld", tcount);
  1259.         }
  1260.     } while (n == blklen);
  1261.     if ( !Fromcu)
  1262.         signal(SIGINT, SIG_IGN);
  1263.  
  1264.     for (;;) {
  1265.         stohdr(Txpos);
  1266.         zsbhdr(ZEOF, Txhdr);
  1267.         switch (getinsync(0)) {
  1268.         case ZACK:
  1269.             continue;
  1270.         case ZRPOS:
  1271.             goto somemore;
  1272.         case ZRINIT:
  1273.             return OK;
  1274.         case ZSKIP:
  1275.             fclose(in);
  1276.             return c;
  1277.         default:
  1278.             fclose(in);
  1279.             return ERROR;
  1280.         }
  1281.     }
  1282. }
  1283.  
  1284. /*
  1285.  * Respond to receiver's complaint, get back in sync with receiver
  1286.  */
  1287. getinsync(flag)
  1288. {
  1289.     register c;
  1290.  
  1291.     for (;;) {
  1292.         if (Testattn) {
  1293.             printf("\r\n\n\n***** Signal Caught *****\r\n");
  1294.             Rxpos = 0; c = ZRPOS;
  1295.         } else
  1296.             c = zgethdr(Rxhdr, 0);
  1297.         switch (c) {
  1298.         case ZCAN:
  1299.         case ZABORT:
  1300.         case ZFIN:
  1301.         case TIMEOUT:
  1302.             return ERROR;
  1303.         case ZRPOS:
  1304.             if (Lastn >= 0 && Lastread == Rxpos) {
  1305.                 Dontread = TRUE;
  1306.             } else {
  1307.                 clearerr(in);    /* In case file EOF seen */
  1308.                 fseek(in, Rxpos, 0);
  1309.             }
  1310.             Lrxpos = Txpos = Rxpos;
  1311.             return c;
  1312.         case ZACK:
  1313.             Lrxpos = Rxpos;
  1314.             if (flag || Txpos == Rxpos)
  1315.                 return ZACK;
  1316.             continue;
  1317.         case ZRINIT:
  1318.         case ZSKIP:
  1319.             fclose(in);
  1320.             return c;
  1321.         case ERROR:
  1322.         default:
  1323.             zsbhdr(ZNAK, Txhdr);
  1324.             continue;
  1325.         }
  1326.     }
  1327. }
  1328.  
  1329.  
  1330. /* Say "bibi" to the receiver, try to do it cleanly */
  1331. saybibi()
  1332. {
  1333.     for (;;) {
  1334.         stohdr(0L);
  1335.         zsbhdr(ZFIN, Txhdr);
  1336.         switch (zgethdr(Rxhdr, 0)) {
  1337.         case ZFIN:
  1338.             sendline('O'); sendline('O'); flushmo();
  1339.         case ZCAN:
  1340.         case TIMEOUT:
  1341.             return;
  1342.         }
  1343.     }
  1344. }
  1345.  
  1346. /* Local screen character display function */
  1347. bttyout(c)
  1348. {
  1349.     if (Verbose)
  1350.         putc(c, stderr);
  1351. }
  1352.  
  1353. /* Send command and related info */
  1354. zsendcmd(buf, blen)
  1355. char *buf;
  1356. {
  1357.     register c, errors;
  1358.     long cmdnum;
  1359.  
  1360.     cmdnum = getpid();
  1361.     errors = 0;
  1362.     for (;;) {
  1363.         stohdr(cmdnum);
  1364.         Txhdr[ZF0] = Cmdack1;
  1365.         zsbhdr(ZCOMMAND, Txhdr);
  1366.         zsdata(buf, blen, ZCRCW);
  1367. listen:
  1368.         Rxtimeout = 100;        /* Ten second wait for resp. */
  1369.         c = zgethdr(Rxhdr, 1);
  1370.  
  1371.         switch (c) {
  1372.         case ZRINIT:
  1373.             continue;
  1374.         case ERROR:
  1375.         case TIMEOUT:
  1376.             if (++errors > Cmdtries)
  1377.                 return ERROR;
  1378.             continue;
  1379.         case ZCAN:
  1380.         case ZABORT:
  1381.         case ZFIN:
  1382.         case ZSKIP:
  1383.         case ZRPOS:
  1384.             return ERROR;
  1385.         default:
  1386.             if (++errors > 10)
  1387.                 return ERROR;
  1388.             continue;
  1389.         case ZCOMPL:
  1390.             Exitcode = Rxpos;
  1391.             saybibi();
  1392.             return OK;
  1393.         case ZRQINIT:
  1394.             vfile("******** RZ *******");
  1395.             system("rz");
  1396.             vfile("******** SZ *******");
  1397.             goto listen;
  1398.         }
  1399.     }
  1400. }
  1401.  
  1402. /*
  1403.  * If called as sb use YMODEM protocol
  1404.  */
  1405. chkinvok(s)
  1406. char *s;
  1407. {
  1408.     register char *p;
  1409.  
  1410.     p = s;
  1411.     while (*p == '-')
  1412.         s = ++p;
  1413.     while (*p)
  1414.         if (*p++ == '/')
  1415.             s = p;
  1416.     if (*s == 'v') {
  1417.         Verbose=1; ++s;
  1418.     }
  1419.     Progname = s;
  1420.     if (s[0]=='s' && s[1]=='b') {
  1421.         Nozmodem = TRUE; blklen=KSIZE;
  1422.     }
  1423.     if (s[0]=='s' && s[1]=='x') {
  1424.         Modem = TRUE;
  1425.     }
  1426. }
  1427. /* End of sz.c */
  1428.